package main

import (
	"fmt"
	"github.com/gookit/goutil/dump"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"reflect"
	"store_appoint_keys_to_redis/store_data_to_redis"
	"time"
)

// Notice Redis标签要存储的标签的值一定要跟json标签的字段一样！！！
type User struct {
	ID          int       `json:"id" gorm:"primaryKey;column:id" redis:"id"`
	Name        string    `json:"name" gorm:"column:name" redis:"name"`
	Email       string    `json:"email" gorm:"column:email" redis:"email"`
	PhoneNumber string    `json:"phoneNumber" gorm:"phone_number" redis:"phoneNumber"`
	CreateAt    time.Time `json:"createAt" gorm:"column:create_at;default:null" redis:"-"`
	UpdateAt    time.Time `json:"updateAt" gorm:"column:update_at;default:null" redis:"-"`
}

func main() {
	dsn := "root:123@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	fmt.Println("db: ", db)

	// Notice 假如实际中 create_at、update_at 这2个字段没有用处，直接不查询这2个字段
	// Notice 注意使用反射的话，需要是一个结构体切片，不能是一个结构体指针的切片！！！
	users := make([]User, 0)

	err = db.Table("user").Select("id", "name", "email", "phone_number").Find(&users).Error
	if err != nil {
		panic(err)
	}

	dump.P(users) // 数据库中有2条数据，这样的到的结果还是会有CreateAt与UpdateAt
	/*
		[]*main.User [ #len=2
		  &main.User {
		    ID: int(1),
		    Name: string("naruto"), #len=6
		    Email: string("123@qq.com"), #len=10
		    PhoneNumber: string("13333333333"), #len=11
		    CreateAt: time.Time {
		      wall: uint64(0),
		      ext: int64(0),
		      loc: *time.Location<nil>,
		    },
		    UpdateAt: time.Time {
		      wall: uint64(0),
		      ext: int64(0),
		      loc: *time.Location<nil>,
		    },
		  },
		  &main.User {
		    ID: int(2),
		    Name: string("sasuke"), #len=6
		    Email: string("222@qq.com"), #len=10
		    PhoneNumber: string("16666666666"), #len=11
		    CreateAt: time.Time {
		      wall: uint64(0),
		      ext: int64(0),
		      loc: *time.Location<nil>,
		    },
		    UpdateAt: time.Time {
		      wall: uint64(0),
		      ext: int64(0),
		      loc: *time.Location<nil>,
		    },
		  },
		]
	*/

	// map的key根据redis这个标签中的值获取
	sliceMap := getRedisTagSliceMapByReflect(users)
	dump.P(sliceMap)
	/*
		[]map[string]interface {} [ #len=2
		  map[string]interface {} { #len=4
		    "id": int(1),
		    "name": string("naruto"), #len=6
		    "email": string("123@qq.com"), #len=10
		    "phoneNumber": string("13333333333"), #len=11
		  },
		  map[string]interface {} { #len=4
		    "name": string("sasuke"), #len=6
		    "email": string("222@qq.com"), #len=10
		    "phoneNumber": string("16666666666"), #len=11
		    "id": int(2),
		  },
		]
	*/

	// 最后使用pipeline写入到redis中即可
	errStore := store_data_to_redis.StoreUsersToRedis(sliceMap)
	if errStore != nil {
		fmt.Println("errStore: ", errStore)
	} else {
		fmt.Println("用户数据保存到redis成功!!!")
	}
}

func getRedisTagSliceMapByReflect(users []User) []map[string]interface{} {

	sliceMap := make([]map[string]interface{}, 0)

	for _, data := range users {
		val := reflect.ValueOf(data)
		typ := val.Type()
		currMp := make(map[string]interface{}, 0)
		for i := 0; i < val.NumField(); i++ {
			field := typ.Field(i)
			tag := field.Tag.Get("redis")

			if tag != "-" {
				fmt.Printf("Storing field %s with value %v in Redis\n", field.Name, val.Field(i).Interface())
				// field.Name 跟结构体的属性一样，都是大写开头，不用它
				//storeMap[field.Name] = value.Field(i).Interface()
				currMp[tag] = val.Field(i).Interface()
			}
		}
		sliceMap = append(sliceMap, currMp)
	}

	return sliceMap
}
